/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

//package server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.net.Socket;


import java.io.*;
import java.util.Iterator;
import java.util.List;
import org.jdom.*;
import org.jdom.input.*;
import org.jdom.output.*;
import java.util.Scanner;


/**
 *
 * @author josegarcia
 */
class Request {
	private Socket socket = null;
	private Object message;
	private long time;

	public SAXBuilder builder;
	public Element structureRoot;
	public Element requestRoot;
	public RandomAccessFile hashFile;
	public final int EMPTY_SLOT = 0;
	public final int FULL_SLOT = 1;
	public final int RESERVED_SLOT = 2;
	public int registerSize = 0;
	public int initialCapacity = 1000;



	public String messaje(){
		return message.toString();
	}
	public long time(){
		return time;
	}
	public Request(Socket socket,Object message, long time){
		builder = new SAXBuilder(false);
		requestRoot = ((Document)message).getRootElement();

		try
		{
			if(requestRoot.getAttributeValue("type")==null)
				throw new XMLParseException("Request missing type");

			message = requestRoot.getAttributeValue("type");
		}catch(Exception e)
		{
			System.out.println("Clavo en Request: \n"+e.getMessage());
		}

		this.socket=socket;
		this.message=message;
		this.time=time;
	}
	public String toString(){
		String st=message.toString()+"\n\t"+"Received at: "+time+"\n\t"+"From"+socket.getInetAddress().toString();
		return st;
	}
	
	public void execute(Boolean listening){

		try{
			// Create file
			FileWriter fstream = new FileWriter("res/out.txt", true);
			BufferedWriter outText = new BufferedWriter(fstream);

			//Close the output stream
			try
			{
				Document structure = builder.build("res/structure.xml");
				structureRoot = structure.getRootElement();
				hashFile = new RandomAccessFile(new File("res/Hash.txt"),"rwd");
				Iterator structureFieldsListI = structureRoot.getChildren("Field").iterator();
				registerSize += 4;
				while(structureFieldsListI.hasNext())
				{
					Element structureElementField = (Element)structureFieldsListI.next();
					
					if(structureElementField.getAttributeValue("type").equals("string"))
					{
						registerSize += 60;
					}
					else if(structureElementField.getAttributeValue("type").equals("int"))
					{
						registerSize += 4;
					}
				}
				parseRequest(requestRoot,structureRoot,hashFile);
				hashFile.close();

			}catch(Exception e)
			{
				System.out.println("Clavo en execute: \n"+e.getMessage());
			}




			PrintWriter out=null;
			BufferedReader in=null;


			out = new PrintWriter(socket.getOutputStream(), true);
			in = new BufferedReader(new InputStreamReader(socket.getInputStream()));



			if(message.toString().equals("EXIT")){
				out.close();
				in.close();
				socket.close();

			}
			
			String output="From Server: "+message.toString();

			out.println(output);
			String report="Request of:"+ message.toString()+" from: "+socket.getInetAddress().toString()+"\n\r\tReceived at "+time+"\n\r\tExcecuted at "+System.currentTimeMillis()+"\n\r\n\r";
			outText.write(report);

			outText.close();





		}catch(IOException e){
			System.out.println("Error IO en servidor");
			System.exit(-1);
		}


	}
	public void parseRequest(Element requestRoot, Element structureRoot,RandomAccessFile archivoHash)
	{
		try {
			
			//Verificar tipo de request
			if(requestRoot.getAttributeValue("type")==null)
				throw new XMLParseException("Request missing type");
			
			System.out.println("El request es de tipo: "+requestRoot.getAttributeValue("type")+"\n");


			//Request de tipo add
			if(requestRoot.getAttributeValue("type").equals("add"))
			{
				List requestElements = requestRoot.getChildren(structureRoot.getAttributeValue("structureName"));
				Iterator requestElementsI = requestElements.iterator();
				//System.out.println("Numero de elementos a procesar: "+requestElements.size());

				while(requestElementsI.hasNext())
				{
					
					System.out.println("A ingresar: \t"+structureRoot.getAttributeValue("structureName"));
					Element requestElement = (Element)requestElementsI.next();
					Iterator structureFieldsListI = structureRoot.getChildren("Field").iterator();

					//Verificacion de todos los required fields
					while(structureFieldsListI.hasNext())
					{
						Element field = (Element)structureFieldsListI.next();
						if(field.getAttributeValue("required").equals("true"))
						{
							if(requestElement.getChild(field.getValue())==null)
								throw new XMLParseException("Request missing "+field.getValue()+" field");
						}
					}
					writeToFile(requestElement,structureRoot,hashFile);
					
				}
			}

			else
			{
				throw new XMLParseException("Wrong request type");
			}

		}catch (IOException e){
			System.out.println("IOException");
			e.printStackTrace();
		}
		catch (XMLParseException e){
			System.out.println("XMLParseException");
			e.printStackTrace();
		}
	}

	public void writeToFile(Element requestElement,Element structureRoot,RandomAccessFile hashFile) throws IOException
	{
		Iterator structureFieldsListI = structureRoot.getChildren("Field").iterator();
		while(structureFieldsListI.hasNext())
		{
			Element structureElementField = (Element)structureFieldsListI.next();
			if(structureElementField.getAttributeValue("primaryKey")!=null)
			{
				int hashCode = requestElement.getChild(structureElementField.getValue()).getValue().hashCode()%initialCapacity;
				long offset = hashCode*registerSize;
				System.out.println("registerSize: "+registerSize);
				System.out.println("offset original: "+offset);
				try
				{
					hashFile.seek(offset);
					System.out.println("offset antes: "+offset);
					int slotStatus = hashFile.readInt();
					//System.out.println("slotStatus: "+slotStatus);
					//System.out.println("offset despues: "+offset);
					boolean endFlag = false;
					while(!endFlag)
					{
						//System.out.println("pos: "+offset);
						if(slotStatus == FULL_SLOT)
						{
							//System.out.println("encontro un full slot");
							//System.out.println("dentro del full: "+offset);
							offset += registerSize;
							hashFile.seek(offset);
							slotStatus = hashFile.readInt();
							//System.out.println("slotStatus dentro del full: "+slotStatus);
						}
						else
						{
							endFlag = true;
						}
					}
					hashFile.seek(offset);
					hashFile.writeInt(FULL_SLOT);
					//System.out.println("offset final: "+offset);
				}
				catch(Exception e)
				{
					//System.out.println("entro a excepcion");
					//System.out.println("offset despues: "+offset);
					hashFile.seek(offset);
					hashFile.writeInt(FULL_SLOT);
				}
			}
		}
		
		//System.out.println("size: "+hashFile.length());
		structureFieldsListI = structureRoot.getChildren("Field").iterator();
		
		while(structureFieldsListI.hasNext())
		{
			Element structureElementField = (Element)structureFieldsListI.next();
			
			if(structureElementField.getAttributeValue("type").equals("string"))
			{
				String toWrite = requestElement.getChild(structureElementField.getValue()).getValue();
				if(toWrite.length()<60)
				{
					hashFile.writeChars(toWrite);
					for (int i=toWrite.length();i<60;i++)
						toWrite = toWrite+" ";
				}
				else
				{
					toWrite= toWrite.substring(0,60);
					hashFile.writeChars(toWrite);
				}			
				
				System.out.println("string: "+requestElement.getChild(structureElementField.getValue()).getValue());
			}
			else if(structureElementField.getAttributeValue("type").equals("int"))
			{
				int toWrite = Integer.parseInt(requestElement.getChild(structureElementField.getValue()).getValue());
				hashFile.writeInt(toWrite);
				System.out.println("int: "+requestElement.getChild(structureElementField.getValue()).getValue());	
			}
		}
	}
	
	public int locate(int key,RandomAccessFile hashFile)
	{
		return 0;
	}
}
